/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @file
 * AgentEventRequest.h
 *
 */

#ifndef _AGENT_EVENT_REQUEST_H_
#define _AGENT_EVENT_REQUEST_H_

#include "memory.h"
#include "AgentBase.h"
#include "RequestModifier.h"
#include "ExceptionManager.h"

namespace jdwp {

    /**
     * The class is a container for the information about a certain event 
     * request.
     * It includes event kind, request ID, suspend policy and event modifiers.
     */
    class AgentEventRequest : public AgentBase {

    public:

        /**
         * A constructor.
         *
         * @param kind     - event kind
         * @param suspend  - suspend policy
         * @param modCount - number of modifiers
         */
        AgentEventRequest(
            jdwpEventKind kind,
            jdwpSuspendPolicy suspend,
            jint modCount = 0
        );

        /**
         * A destructor.
         */
        virtual ~AgentEventRequest();

        /**
         * Assigns request ID.
         *
         * @param id - request ID
         */
        void SetRequestId(RequestID id) { m_requestId = id; }

        /**
         * Assigns expiration flag of the event request.
         *
         * @param flag - expire flag
         */
        void SetExpired(bool flag) { m_isExpired = flag; }

        /**
         * Assigns a new modifier at the given location in the list of modifiers.
         *
         * @param modifier - pointer to modifier
         * @param i        - index of place in the array of modifiers
         */
        virtual void AddModifier(RequestModifier* modifier, jint i) {
            JDWP_ASSERT(i < m_modifierCount);
            m_modifiers[i] = modifier;
        }

        /**
         * Returns request ID.
         */
        RequestID GetRequestId() const {
            return m_requestId;
        }

        /**
         * Returns event kind of this request.
         */
        jdwpEventKind GetEventKind() const {
            return m_eventKind;
        }

        /**
         * Returns suspend policy of this request.
         */
        jdwpSuspendPolicy GetSuspendPolicy() const {
            return m_suspendPolicy;
        }

        /**
         * Returns number of modifiers of this event request.
         */
        jint GetModifierCount() const {
            return m_modifierCount;
        }

        /**
         * Returns whether this event request is expired.
         */
        bool IsExpired() const {
            return m_isExpired;
        }

        /**
         * Compares ID of this request with the given ID.
         *
         * @param id - value to compare with
         */
        bool operator == (const RequestID id) const {
            return m_requestId == id;
        }

        /**
         * Applies accompanied modifiers for filtering this event.
         *
         * @param jni   - the JNI interface pointer
         * @param eInfo - event information
         */
        virtual bool ApplyModifiers(JNIEnv* jni, EventInfo &eInfo);

        /**
         * Gets the Java thread from the corresponding ThreadOnly modifier.
         */
        virtual jthread GetThread() const;

        /**
         * Gets the FieldOnly modifier from the saved list of modifiers.
         */
        FieldOnlyModifier* GetField() const;

        /**
         * Gets the LocationOnly modifier from the saved list of modifiers.
         */
        LocationOnlyModifier* GetLocation() const;

    protected:

        bool m_isExpired;
        jint m_modifierCount;
        RequestModifier** m_modifiers;

    private:

        RequestID m_requestId;
        jdwpEventKind m_eventKind;
        jdwpSuspendPolicy m_suspendPolicy;

    };

    /**
     * The class implements step event request that handles single step
     * events generated by target VM.
     */
    class StepRequest : public AgentEventRequest {

    public:

        /**
         * A constructor.
         *
         * @param suspend  - suspend policy
         * @param modCount - number of modifiers
         */
        StepRequest(jdwpSuspendPolicy suspend, jint modCount)
          :
            AgentEventRequest(JDWP_EVENT_SINGLE_STEP, suspend, modCount),
            m_thread(0),
            m_size(0),
            m_depth(0),
            m_frameCount(0),
            m_lineNumber(0),
            m_isNative(false),
            m_isActive(false),
            m_framePopRequest(0),
            m_methodEntryRequest(0)
        { JDWP_ASSERT(modCount > 0); }

        /**
         * A destructor.
         */
        ~StepRequest();

        /**
         * Handles step on frame pop event.
         *
         * @param jni - the JNI interface pointer
         */
        int OnFramePop(JNIEnv *jni);

        /**
         * Handles step on method-entry event.
         *
         * @param jni   - the JNI interface pointer
         * @param eInfo - event information
         */
        void OnMethodEntry(JNIEnv *jni, EventInfo &eInfo);

        /**
         * Initializes step event request.
         * @param jni    - the JNI interface pointer
         * @param thread - step thread
         * @param size   - step size
         * @param depth  - step depth
         */
        int Init(JNIEnv* jni, jthread thread, jint size, jint depth)
           ;

        /**
         * Applies accompanied modifiers for filtering the given event.
         */
        bool ApplyModifiers(JNIEnv* jni, EventInfo &eInfo)
           ;

        /**
         * Gets the associated Java thread.
         */
        jthread GetThread() const {
            return m_thread;
        }

        /**
         * Restores a step request after a PopFrame process.
         *
         * @exception AgentException is thrown, if any error occurs.
         */
        int Restore();

    private:

        /**
         * Gets current line of the current step location.
         *
         * @return Java integer (jint).
         */
        jint GetCurrentLine();

        /**
         * Enables or disables single step event generation in the target VM.
         *
         * @param enable - boolean value for the step event notification mode
         */
        void ControlSingleStep(bool enable);

        /**
         * Checks whether at least one modifier can be applied.
         *
         * @param jni   - the JNI interface pointer
         * @param eInfo - event-request descriptor
         */
        bool IsClassApplicable(JNIEnv* jni, EventInfo &eInfo);

        jthread m_thread;
        jint m_size;
        jint m_depth;
        jint m_frameCount;
        jint m_lineNumber;
        bool m_isNative;
        bool m_isActive;
        AgentEventRequest* m_framePopRequest;
        AgentEventRequest* m_methodEntryRequest;

    };

}

#endif // _AGENT_EVENT_REQUEST_H_
